#ifndef SceneManager_h__
#define SceneManager_h__

#include <vector>
#include "DX11Renderer.h"
#include "RenderActor.h"
#include "ModelLoader.h"
#include "Camera.h"

namespace PracEng
{
#pragma pack(push,1)
	struct CB_VS_PER_OBJECT
	{
		PEMatrix m_mWorldViewProjection;
		PEMatrix m_mWorld;
	};

	struct CB_PS_PER_OBJECT
	{
		//PEFloat3 m_vEyePosition;
		float m_fSpecExp;
		float m_fSpecIntensity;
		bool m_bUseNormalMap;
		bool pad[7];
		PEMatrix m_mWorld;
	};

	struct CB_GBUFFER_UNPACK
	{
		PEVector4 PerspectiveValues;
		PEMatrix  ViewInv;
	};

	struct CB_DIRECTIONAL
	{
		PEVector3 vAmbientLower;
		float pad;
		PEVector3 vAmbientRange;
		float pad2;
		PEVector3 vDirToLight;
		float pad3;
		PEVector3 vDirectionalColor;
		float pad4;
		PEMatrix ToShadowSpace;
		PEVector4 ToCascadeSpace[3];
	};

	struct CB_POINT_LIGHT_DOMAIN
	{
		PEMatrix WorldViewProj;
	};

	struct CB_POINT_LIGHT_PIXEL
	{
		PEVector3 PointLightPos;
		float PointLightRangeRcp;
		PEVector3 PointColor;
		float pad;
		PEFloat2 LightPerspectiveValues;
		float pad2[2];
	};

	struct CB_SPOT_LIGHT_DOMAIN
	{
		PEMatrix WolrdViewProj;
		float fSinAngle;
		float fCosAngle;
		float pad[2];
	};

	struct CB_SPOT_LIGHT_PIXEL
	{
		PEVector3 SpotLightPos;
		float SpotLightRangeRcp;
		PEVector3 vDirToLight;
		float SpotCosOuterCone;
		PEVector3 SpotColor;
		float SpotCosConeAttRange;
		PEMatrix ToShadowMap;
	};

	struct CB_DOWN_SCALE
	{
		UINT nWidth;
		UINT nHeight;
		UINT nTotalPixels;
		UINT nGroupSize;
		float fAdaptation;
		float fBloomThreshold;
		UINT pad[2];
	};

	struct CB_HDR_TO_LDR
	{
		float fMiddleGrey;
		float fLumWhiteSqr;
		float fBloomScale;
		UINT pad[1];
		float ProjectionValues[2];
		float fDOFFarStart;
		float fDOFFarRangeRcp;
	} ;

	struct CB_BLUR
	{
		UINT numApproxPasses;
		float fHalfBoxFilterWidth;			// w/2
		float fFracHalfBoxFilterWidth;		// frac(w/2+0.5)
		float fInvFracHalfBoxFilterWidth;	// 1-frac(w/2+0.5)
		float fRcpBoxFilterWidth;			// 1/w
		UINT pad[3];
	};

	struct CB_SSAO_DOWN_SCALE
	{
		UINT nWidth;
		UINT nHeight;
		float fHorResRcp;
		float fVerResRcp;
		PEVector4 ProjParams;
		PEMatrix ViewMatrix;
		float fOffsetRadius;
		float fRadius;
		float fMaxDepth;
		UINT pad;
	};

#pragma pack(pop)

	class Light
	{
	public:
		Light() :m_LightPosition(0), m_LightColor(1){}
		~Light(){}

		void SetLightPos(const PEVector3& Pos);
		void SetLightPos(float x, float y, float z);
		void SetLightColor(float R, float G, float B);
		void SetLightColor(const PEVector3& color);

		inline PEVector3 GetLightColor(){ return m_LightColor; }
		inline PEVector3 GetLightPos(){ return m_LightPosition; }

		virtual void SetLightDir(const PEVector3& Direction){}
		virtual void SetLightDir(float x, float y, float z){}
		virtual PEVector3 GetLightDir(){ return PEVector3(0, 0, 0); }
		virtual void SetLightRange(float Radius){}
		virtual float GetLightRange(){ return 0; }

		PEVector3 m_LightPosition;
		PEVector3 m_LightColor;
		PEMatrix m_ShadowMapViewProj;
		unsigned int m_LightIdx;
	};

	class DirectionalLight : public Light
	{
	public:
		virtual void SetLightDir(const PEVector3& Direction) override;
		virtual void SetLightDir(float x, float y, float z) override;
		virtual PEVector3 GetLightDir() override;
	private:
		PEVector3 m_Dir;
	};

	class PointLight : public Light
	{
	public:
		virtual void SetLightRange(float Radius) override;
		virtual float GetLightRange() override;
	private:
		float m_Range;
	};

	class SpotLight : public Light
	{
	public:
		virtual void SetLightDir(const PEVector3& Direction) override;
		virtual void SetLightDir(float x, float y, float z) override;
		virtual PEVector3 GetLightDir() override;
		virtual void SetLightRange(float Radius) override;
		virtual float GetLightRange() override;

		void SetAngle(float fInnerAngle, float fOutAngle);
		float GetInnerAngle();
		float GetOuterAngle();

	private:
		PEVector3 m_Dir;
		float m_Range;
		float m_InnerAngle;
		float m_OuterAngle;
	};

	class CascadeShadowMapMatrixs
	{
	public:

		CascadeShadowMapMatrixs();
		~CascadeShadowMapMatrixs();

		void Init(int iShadowMapSize, Camera* pCam);

		void Update(const PEVector3& vDirectionalDir);

		// Change the antiflicker state
		void SetAntiFlicker(bool bIsOn) { m_bAntiFlickerOn = bIsOn; }

		const PEMatrix* GetWorldToShadowSpace() const { return &m_WorldToShadowSpace; }
		const PEMatrix* GetWorldToCascadeProj(int i) const { return &m_arrWorldToCascadeProj[i]; }
		const PEVector4 GetToCascadeOffsetX() const { return m_vToCascadeOffsetX; }
		const PEVector4 GetToCascadeOffsetY() const { return m_vToCascadeOffsetY; }
		const PEVector4 GetToCascadeScale() const { return m_vToCascadeScale; }

		static const int m_iTotalCascades = 3;

	private:

		// Extract the frustum corners for the given near and far values
		void ExtractFrustumPoints(float fNear, float fFar, PEVector3* arrFrustumCorners);

		// Extract the frustum bounding sphere for the given near and far values
		void ExtractFrustumBoundSphere(float fNear, float fFar, PEVector3& vBoundCenter, float& fBoundRadius);

		// Test if a cascade needs an update
		bool CascadeNeedsUpdate(const PEMatrix& mShadowView, int iCascadeIdx, const PEVector3& newCenter, PEVector3& vOffset);

		bool m_bAntiFlickerOn;
		int m_iShadowMapSize;
		float m_fCascadeTotalRange;
		float m_arrCascadeRanges[4];

		PEVector3 m_vShadowBoundCenter;
		float m_fShadowBoundRadius;
		PEVector3 m_arrCascadeBoundCenter[m_iTotalCascades];
		float m_arrCascadeBoundRadius[m_iTotalCascades];

		PEMatrix m_WorldToShadowSpace;
		PEMatrix m_arrWorldToCascadeProj[m_iTotalCascades];

		PEVector4 m_vToCascadeOffsetX;
		PEVector4 m_vToCascadeOffsetY;
		PEVector4 m_vToCascadeScale;

		Camera* m_Cam;
	};

	class SceneManager
	{
	public:
		SceneManager();
		virtual ~SceneManager();

		virtual bool Init(DX11Renderer* pRenderer, Camera* pCamera);
		virtual bool Exit();
		virtual void Update(double DeltaTime);

		virtual void InitGBuffer();
		virtual void PostRendering();
		virtual void PreRendering();
		virtual void RenderScene();

		void GenerateShadowMap();
		void GenSpotLightShadowMap();
		void GenPointLightShadowMap();
		void GenCascadeShadowMap();

		void VisualizeGBuffer(ID3D11DeviceContext* pd3dImmediateContext);

		void DrawActors();
		void DoLighting();
		void DoPostProcessing();

		void HDRToLDR();
		void DownSampleRT();
		void CalcLum();
		void Adaption();
		void Bloom();
		void Blur();

		void DoSSAO();

		void PrepareForLights();
		void DoDirectionLight(PEVector3& AmbientLower, PEVector3& AmbientUpper, DirectionalLight* DirLight);
		void LightEnd();
		void DirectionLights();
		void PointLights(bool bShowVolume = false);
		void DoPointLight(PointLight* PtLight, bool bShowVolume);
		void SpotLights(bool bShowVolume = false);
		void DoSpotLight(SpotLight* SptLight, bool bShowVolume);

		void DebugGBuffer();
		void DebugPointLightVolume();
		

		void AddActor(RenderActor* Actor);
		void AddActorsToScene(std::vector<OBJModel* >& objMods);

		void AddDirLight(DirectionalLight* DLight);
		void AddPointLight(PointLight* PtLight);
		void AddSpotLight(SpotLight* SptLight);

		bool bShowGbuffer;
		bool bShowLightVolume;
		ID3D11Texture2D* GetColorTexture() { return m_ColorSpecIntensityRT; }
		ID3D11DepthStencilView* GetDepthDSV() { return m_DepthStencilDSV; }
		ID3D11DepthStencilView* GetDepthReadOnlyDSV() { return m_DepthStencilReadOnlyDSV; }
		ID3D11ShaderResourceView* GetDepthView() { return m_DepthStencilSRV; }
		ID3D11ShaderResourceView* GetColorView() { return m_ColorSpecIntensitySRV; }
		ID3D11ShaderResourceView* GetNormalView() { return m_NormalSRV; }
		ID3D11ShaderResourceView* GetSpecPowerView() { return m_SpecPowerSRV; }
		ID3D11Buffer* GetGBufferUnpackCB() { return m_pGBufferUnpackCB; }
		ID3D11Buffer* GetDirLightCB(){ return m_pDirLightCB; }
		ID3D11RasterizerState* GetWireFrameRS(){ return m_WireFrameRS; }
		ID3D11RasterizerState* GetSolidFrameRS(){ return m_SolidRS; }
		ID3D11DepthStencilState* GetNoDepthWLessState(){ return m_NoDepthWriteLessStencilMaskState; }
		ID3D11DepthStencilState* GetNoDepthWGreatState() { return m_NoDepthWriteGreatherStencilMaskState; }
		ID3D11BlendState* GetAdditiveBlendState(){ return m_GeneralBS; }
		ID3D11RasterizerState* GetNoDepthClipFrontRS(){ return m_NoDepthClipFrontRS; }
		ID3D11SamplerState* GetPointSamplerState(){ return m_pSampPoint; }
		ID3D11SamplerState* GetLinearSamplerState(){ return m_pSampLinear; }

		//////////////
		void UseCustomNormalMap(bool bUse);
		
	protected:
		std::vector<RenderActor*> Actors;
		DX11Renderer* m_DX11Render;
		Camera* m_Camera;

		std::vector<DirectionalLight* > m_DirectionalLights;
		std::vector<PointLight* > m_PointLights;
		std::vector<SpotLight* > m_SpotLights;

		std::vector<ID3D11Texture2D* > m_SpotDepthStencilRT;
		std::vector<ID3D11DepthStencilView* > m_SpotDepthStencilDSV;
		std::vector<ID3D11ShaderResourceView* > m_SpotDepthStencilSRV;

		std::vector<ID3D11Texture2D* > m_PointDepthStencilRT;
		std::vector<ID3D11DepthStencilView* > m_PointDepthStencilDSV;
		std::vector<ID3D11ShaderResourceView* > m_PointDepthStencilSRV;

		// Cascaded shadow maps
		std::vector<ID3D11Texture2D* >  m_pCascadedDepthStencilRT;
		std::vector<ID3D11DepthStencilView* > m_pCascadedDepthStencilDSV;
		std::vector<ID3D11ShaderResourceView* > m_pCascadedDepthStencilSRV;

		// Cascaded shadow maps matrices
		CascadeShadowMapMatrixs m_CascadedMatrix;

		//shadowmap
		ID3D11InputLayout* m_pShadowGenVSLayout;

		ID3D11Buffer* m_pSpotShadowGenVertexCB;
		ID3D11VertexShader* m_pSpotShadowGenVertexShader;
		
		ID3D11VertexShader* m_pPointShadowGenVertexShader;
		ID3D11GeometryShader* m_pPointShadowGenGeometryShader;
		ID3D11Buffer* m_pPointShadowGenGeometryCB;

		ID3D11VertexShader* m_pCascadedShadowGenVertexShader;
		ID3D11GeometryShader* m_pCascadedShadowGenGeometryShader;
		ID3D11Buffer* m_pCascadedShadowGenGeometryCB;

		//GBuffer shader
		ID3D11Buffer* m_pSceneVertexShaderCB;
		ID3D11Buffer* m_pScenePixelShaderCB;

		ID3D11VertexShader* m_pSceneVertexShader;
		ID3D11InputLayout* m_pSceneVSLayout;
		ID3D11PixelShader* m_pScenePixelShader;

		
		ID3D11PixelShader* m_pDirLightPixelShader;
		ID3D11VertexShader* m_pDirLightVertexShader;

		ID3D11PixelShader* m_pGBufferVisPixelShader;
		ID3D11VertexShader* m_pGBufferVisVertexShader;

		ID3D11VertexShader* m_pPointLightVertexShader;
		ID3D11HullShader* m_pPointLightHullShader;
		ID3D11DomainShader* m_pPointLightDomainShader;
		ID3D11PixelShader* m_pPointLightPixelShader;
		ID3D11PixelShader* m_pDebugLightPixelShader;
		ID3D11Buffer* m_pPointLightDomainCB;
		ID3D11Buffer* m_pPointLightPixelCB;

		ID3D11VertexShader* m_pSpotLightVertexShader;
		ID3D11HullShader* m_pSpotLightHullShader;
		ID3D11DomainShader* m_pSpotLightDomainShader;
		ID3D11PixelShader* m_pSpotLightPixelShader;
		ID3D11Buffer* m_pSpotLightDomainCB;
		ID3D11Buffer* m_pSpotLightPixelCB;

		ID3D11RasterizerState* m_WireFrameRS;
		ID3D11RasterizerState* m_SolidRS;
		ID3D11RasterizerState* m_NoDepthClipFrontRS;
		ID3D11RasterizerState* m_pShadowGenRS;
		ID3D11RasterizerState* m_pCascadedShadowGenRS;

		ID3D11BlendState* m_GeneralBS;

		ID3D11Buffer* m_pGBufferUnpackCB;
		ID3D11Buffer* m_pDirLightCB;

		// GBuffer Textures
		ID3D11Texture2D* m_DepthStencilRT;
		ID3D11Texture2D* m_ColorSpecIntensityRT;
		ID3D11Texture2D* m_NormalRT;
		ID3D11Texture2D* m_SpecPowerRT;

		// GBuffer render views
		ID3D11DepthStencilView* m_DepthStencilDSV;
		ID3D11DepthStencilView* m_DepthStencilReadOnlyDSV;
		ID3D11RenderTargetView* m_ColorSpecIntensityRTV;
		ID3D11RenderTargetView* m_NormalRTV;
		ID3D11RenderTargetView* m_SpecPowerRTV;

		// GBuffer shader resource views
		ID3D11ShaderResourceView* m_DepthStencilSRV;
		ID3D11ShaderResourceView* m_ColorSpecIntensitySRV;
		ID3D11ShaderResourceView* m_NormalSRV;
		ID3D11ShaderResourceView* m_SpecPowerSRV;

		ID3D11DepthStencilState* m_DepthStencilState;
		ID3D11DepthStencilState* m_NoDepthWriteLessStencilMaskState;
		ID3D11DepthStencilState* m_NoDepthWriteGreatherStencilMaskState;

		ID3D11SamplerState*        m_pSampLinear;
		ID3D11SamplerState*        m_pSampPoint;
		ID3D11SamplerState*		   m_pPCFSampler;

		//HDR
		ID3D11Texture2D* m_pHDRTexture;
		ID3D11RenderTargetView* m_HDRRTV;
		ID3D11ShaderResourceView* m_HDRSRV;
		ID3D11Buffer* m_pDownScale1DBuffer;
		ID3D11UnorderedAccessView* m_pDownScale1DUAV;
		ID3D11ShaderResourceView* m_pDownScale1DSRV;
		ID3D11Buffer* m_pAvgLumBuffer;
		ID3D11UnorderedAccessView* m_pAvgLumUAV;
		ID3D11ShaderResourceView* m_pAvgLumSRV;
		ID3D11Buffer* m_pDownScaleCB;
		ID3D11Buffer* m_pFinalPassCB;
		UINT m_nDownScaleGroups;
		float m_fMidGray;
		float m_fWhite;
		float m_fAdaptation;
		float m_fBloomScale;
		float m_fBloomThreshold;
		float m_fDOFFarStart;
		float m_fDOFFarRanceRcp;
		ID3D11Buffer* m_pPrevAvgLumBuffer;
		ID3D11UnorderedAccessView* m_pPrevAvgLumUAV;
		ID3D11ShaderResourceView* m_pPrevAvgLumSRV;

		//
		ID3D11ComputeShader* m_pDownScaleFirstPassCS;
		ID3D11ComputeShader* m_pDownScaleSecondPassCS;
		ID3D11VertexShader* m_pFullScreenQuadVS;
		ID3D11PixelShader* m_pFinalPassPS;

		// Downscaled scene texture
		ID3D11Texture2D* m_pDownScaleRT;
		ID3D11ShaderResourceView* m_pDownScaleSRV;
		ID3D11UnorderedAccessView* m_pDownScaleUAV;

		// Temporary texture
		ID3D11Texture2D* m_pTempRT[2];
		ID3D11ShaderResourceView* m_pTempSRV[2];
		ID3D11UnorderedAccessView* m_pTempUAV[2];

		// Bloom texture
		ID3D11Texture2D* m_pBloomRT;
		ID3D11ShaderResourceView* m_pBloomSRV;
		ID3D11UnorderedAccessView* m_pBloomUAV;

		ID3D11ComputeShader* m_pBloomRevealCS;
		ID3D11ComputeShader* m_HorizontalBlurCS;
		ID3D11ComputeShader* m_VerticalBlurCS;

		// SSAO
		int m_iSSAOSampRadius;
		float m_fSSAORadius;
		ID3D11Buffer* m_pSSAODownscaleCB;
		// SSAO values for usage with the directional light
		ID3D11Texture2D* m_pSSAO_RT;
		ID3D11UnorderedAccessView* m_pSSAO_UAV;
		ID3D11ShaderResourceView* m_pSSAO_SRV;

		// Downscaled depth buffer (1/4 size)
		ID3D11Buffer* m_pMiniDepthBuffer;
		ID3D11UnorderedAccessView* m_pMiniDepthUAV;
		ID3D11ShaderResourceView* m_pMiniDepthSRV;

		// Shaders
		ID3D11ComputeShader* m_pSSAODepthDownscaleCS;
		ID3D11ComputeShader* m_pSSAOComputeCS;

	};
}

#endif // SceneManager_h__
